/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "octospi.h"
#include "usart.h"
#include "gpio.h"
#include "w25q128jv.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int OSPI_W25Q128JV_AutoPollingMemReady(uint32_t timeout);
int OSPI_W25Q128JV_WriteEnable(void);
int OSPI_W25Q128JV_ReadReg(uint8_t reg, uint8_t *v);
int OSPI_W25Q128JV_DeviceID(uint8_t *v);
int OSPI_W25Q128JV_IDNumber(uint8_t *v);
int OSPI_W25Q128JV_Reset(void);
int OSPI_W25Q128JV_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size);
int OSPI_W25Q128JV_EraseSector(uint32_t SectorAddress);
int OSPI_W25Q128JV_PageProgram(uint8_t *pData, uint32_t ReadAddr, uint32_t Size);
int OSPI_W25Q128JV_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size);

int OSPI_W25Q128JV_AutoPollingMemReady(uint32_t timeout)
{
  OSPI_RegularCmdTypeDef sCmd;
  OSPI_AutoPollingTypeDef sCfg;

  memset(&sCmd, 0, sizeof(sCmd));
  memset(&sCfg, 0, sizeof(sCfg));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_STATUS_REG1;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = 0;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  sCfg.Match = 0;
  sCfg.Mask = 0x1;
  sCfg.MatchMode = HAL_OSPI_MATCH_MODE_AND;
  sCfg.Interval = 0x10;
  sCfg.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

  if (HAL_OSPI_AutoPolling(&hospi1, &sCfg, timeout) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_WriteEnable(void)
{
  OSPI_RegularCmdTypeDef sCmd;
  OSPI_AutoPollingTypeDef sCfg;

  memset(&sCmd, 0, sizeof(sCmd));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_WRITE_ENABLE;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  memset(&sCmd, 0, sizeof(sCmd));
  memset(&sCfg, 0, sizeof(sCfg));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_STATUS_REG1;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  sCfg.Match = 0x2;
  sCfg.Mask = 0x2;
  sCfg.MatchMode = HAL_OSPI_MATCH_MODE_AND;
  sCfg.Interval = 0x10;
  sCfg.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

  if (HAL_OSPI_AutoPolling(&hospi1, &sCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_ReadReg(uint8_t reg, uint8_t *v)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = reg;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = 0;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (HAL_OSPI_Receive(&hospi1, (uint8_t *)v, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_DeviceID(uint8_t *v)
{
  OSPI_RegularCmdTypeDef sCmd;
  memset(&sCmd, 0, sizeof(sCmd));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_DEVICE_ID;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = 0;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = 2;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (HAL_OSPI_Receive(&hospi1, (uint8_t *)v, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_IDNumber(uint8_t *v)
{
  OSPI_RegularCmdTypeDef sCmd;
  memset(&sCmd, 0, sizeof(sCmd));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_ID_NUMBER;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = 0;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = 12;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (HAL_OSPI_Receive(&hospi1, (uint8_t *)v, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_Reset(void)
{
  OSPI_RegularCmdTypeDef sCmd;
  memset(&sCmd, 0, sizeof(sCmd));

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_ENABLE_RESET;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_RESET_DEVICE;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
  OSPI_RegularCmdTypeDef sCmd;
  memset(&sCmd, 0, sizeof(sCmd));

  //if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  //{
  //  return OSPI_ERROR;
  //}
#if 1
  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_INPUT_FAST_READ;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_4_LINES;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = ReadAddr;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;

  sCmd.DataMode = HAL_OSPI_DATA_4_LINES;
  sCmd.DummyCycles = 6;
  sCmd.NbData = Size;

#else
  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_FAST_READ_QUAD_OUTPUT;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = ReadAddr;

  sCmd.DataMode = HAL_OSPI_DATA_4_LINES;
  sCmd.DummyCycles = 8;
  sCmd.NbData = Size;
#endif 
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (HAL_OSPI_Receive(&hospi1, pData, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_EraseSector(uint32_t SectorAddress)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  if (OSPI_W25Q128JV_WriteEnable() != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_ERASE_SECTOR;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = SectorAddress;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_ERASE_Block32K(uint32_t SectorAddress)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  if (OSPI_W25Q128JV_WriteEnable() != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_ERASE_Block32K;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = SectorAddress;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_ERASE_Block64K(uint32_t SectorAddress)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  if (OSPI_W25Q128JV_WriteEnable() != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_ERASE_Block64K;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = SectorAddress;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_CHIP_ERASE(void)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  if (OSPI_W25Q128JV_WriteEnable() != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_CHIP_ERASE;

  //sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  //sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  //sCmd.Address = SectorAddress;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;
  
  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_PageProgram(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)
{
  OSPI_RegularCmdTypeDef sCmd;

  memset(&sCmd, 0, sizeof(sCmd));

  if (OSPI_W25Q128JV_WriteEnable() != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_PAGE_PROGRAM;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = ReadAddr;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 1;

  sCmd.DataMode = HAL_OSPI_DATA_1_LINE;
  sCmd.DummyCycles = 0;
  sCmd.NbData = Size;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (HAL_OSPI_Transmit(&hospi1, (uint8_t *)pData, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

int OSPI_W25Q128JV_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
    int ret = OSPI_OK;
    uint32_t end_addr, current_size, current_addr;
    uint8_t *write_data;

    /* Calculation of the size between the write address and the end of the page */
    current_size = W25Q128JV_PAGE_SIZE - (WriteAddr % W25Q128JV_PAGE_SIZE);

    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > Size)
    {
      current_size = Size;
    }

    /* Initialize the address variables */
    current_addr = WriteAddr;
    end_addr = WriteAddr + Size;
    write_data = pData;

    /* Perform the write page by page */
    do
    {
      /* Issue page program command */
      if (OSPI_W25Q128JV_PageProgram(write_data, current_addr, current_size) != OSPI_OK)
      {
          ret = OSPI_ERROR;
      }
      else
      {
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        write_data += current_size;
        current_size = ((current_addr + W25Q128JV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128JV_PAGE_SIZE;
      }
    } while ((current_addr < end_addr) && (ret == OSPI_OK));

    /* Return BSP status */
    return ret;
}

int OSPI_W25Q128JV_EnableMemoryMappedMode(void)
{
  OSPI_RegularCmdTypeDef sCmd;
  OSPI_MemoryMappedTypeDef sMapCfg;

  memset(&sCmd, 0, sizeof(sCmd));
  memset(&sMapCfg, 0, sizeof(sMapCfg));

  if (OSPI_W25Q128JV_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
  {
    return OSPI_ERROR;
  }

  sCmd.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
  sCmd.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
  sCmd.Instruction = W25Q128JV_INPUT_FAST_READ;

  sCmd.AddressMode = HAL_OSPI_ADDRESS_4_LINES;
  sCmd.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
  sCmd.Address = 0;

  //sCmd.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
  //sCmd.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
  //sCmd.AlternateBytes = 0;

  sCmd.DataMode = HAL_OSPI_DATA_4_LINES;
  sCmd.DummyCycles = 6;
  sCmd.NbData = 0;

  if (HAL_OSPI_Command(&hospi1, &sCmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  sMapCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;

  if (HAL_OSPI_MemoryMapped(&hospi1, &sMapCfg) != HAL_OK)
  {
    return OSPI_ERROR;
  }

  return OSPI_OK;
}

#define APPLICATION_ADDRESS 0x90000000UL
typedef void (*appFun)(void);
appFun App;
static void GoToApp(void)
{
    __set_PRIMASK(1);
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    for (int i = 0; i < 8; i++)
    {
        NVIC->ICER[i] = 0xFFFFFFFF;
        NVIC->ICPR[i] = 0xFFFFFFFF;
    }
    __set_PRIMASK(0);
    uint32_t *vector_table = (uint32_t *)APPLICATION_ADDRESS;
    uint32_t stack_point = vector_table[0];
    uint32_t entry_point = vector_table[1];
    App = (appFun)entry_point;
    __set_MSP(stack_point);
    __set_CONTROL(0);
    App();
}

uint8_t flash_page[4096];
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MPU Configuration--------------------------------------------------------*/
  //MPU_Config();
  HAL_MPU_Disable();

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_OCTOSPI1_Init();
  /* USER CODE BEGIN 2 */
  printf("STM32H7B0VB Demo(0x%p). \r\n", main);

  uint8_t w25q128jv_device_id[2];
  uint8_t w25q128jv_id_number[12];
  OSPI_W25Q128JV_DeviceID(w25q128jv_device_id);
  printf("Manufacturer / Device ID: 0x%x 0x%x \r\n", w25q128jv_device_id[0], w25q128jv_device_id[1]);
  if (OSPI_W25Q128JV_Reset() != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_Reset() error !!! \r\n");
  }
  OSPI_W25Q128JV_DeviceID(w25q128jv_device_id);
  //printf("Manufacturer / Device ID: 0x%x 0x%x \r\n", w25q128jv_device_id[0], w25q128jv_device_id[1]);
  
  OSPI_W25Q128JV_IDNumber(w25q128jv_id_number);
  printf("Unique ID Number: ");
  for (int i = 4; i < 12; i++)
  {
    printf("0x%02x ", w25q128jv_id_number[i]);
  }
  printf(" \r\n");

  OSPI_W25Q128JV_ReadReg(W25Q128JV_STATUS_REG1, &w25q128jv_device_id[0]);
  printf("Status Register-1 (0x%x) \r\n", w25q128jv_device_id[0]);
  OSPI_W25Q128JV_ReadReg(W25Q128JV_STATUS_REG2, &w25q128jv_device_id[0]);
  printf("Status Register-2 (0x%x) \r\n", w25q128jv_device_id[0]);
#if 0
  printf("Erase Sector(0) \r\n");
  if (OSPI_W25Q128JV_EraseSector(4096 * 0) != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_EraseSector() error !!! \r\n");
  }

  for (int i = 0; i < sizeof(flash_page); i++)
  {
    if (i % 2 == 0)
      flash_page[i] = 0xa5;
    else
      flash_page[i] = 0x5a;
  }
  //if (OSPI_W25Q128JV_PageProgram(flash_page, 4096 * 0, 256) != OSPI_OK)
  //{
  //  printf("!!! OSPI_W25Q128JV_PageProgram() error !!! \r\n");
  //}
  if (OSPI_W25Q128JV_Write(flash_page, 128, 256) != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_Write() error !!! \r\n");
  }
  if (OSPI_W25Q128JV_Write(flash_page, 128+256, 256) != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_Write() error !!! \r\n");
  }

  memset(flash_page, 0, sizeof(flash_page));
  if (OSPI_W25Q128JV_Read(flash_page, 4096 * 0, sizeof(flash_page)) != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_Read() error !!! \r\n");
  }
  printf("Flash page value: \r\n");
  for (int i = 0; i < sizeof(flash_page); i++)
  {
    printf("0x%02x ", flash_page[i]);
    if ((i + 1) % 16 == 0)
    {
      printf(" \r\n");
    }
  }
  printf(" \r\n");
#endif
  printf("OCTOSPI1->CR(0x%x) \r\n", OCTOSPI1->CR); // 0x1ffffffe;
  if (OSPI_W25Q128JV_EnableMemoryMappedMode() != OSPI_OK)
  {
    printf("!!! OSPI_W25Q128JV_EnableMemoryMappedMode() error !!! \r\n");
  }
  else
  {
#if 0
    uint8_t *pExtFlash = (uint8_t *)0x90000000;
    printf("------------------------------------------------------- \r\n");
    printf("Enable memory mapped mode(0x90000000): \r\n");
    for (int i = 0; i < sizeof(flash_page); i++)
    {
      printf("0x%02x ", *pExtFlash++);
      if ((i + 1) % 16 == 0)
      {
        printf(" \r\n");
      }
    }
    printf(" \r\n");
#endif 
    printf("OCTOSPI1->CR(0x%x) \r\n", OCTOSPI1->CR);
    GoToApp();
  }
  
  uint32_t i = 0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    printf("i = 0x%x \r\n", i++);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /*AXI clock gating */
  RCC->CKGAENR = 0xFFFFFFFF;

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = 64;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/* MPU Configuration */

void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = {0};

  /* Disables the MPU */
  HAL_MPU_Disable();

  /** Initializes and configures the Region and the memory to be protected
  */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x0;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  MPU_InitStruct.SubRegionDisable = 0x87;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  /* Enables the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
